package com.douding.file.controller.admin;

import com.douding.server.dto.FileDto;
import com.douding.server.dto.ResponseDto;
import com.douding.server.exception.ValidatorException;
import com.douding.server.service.FileService;
import com.douding.server.service.TestService;
import com.douding.server.util.Base64ToMultipartFile;
import com.douding.server.util.UuidUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import reactor.core.publisher.Flux;
import sun.misc.BASE64Decoder;

import javax.annotation.Resource;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

/*
    返回json 应用@RestController
    返回页面  用用@Controller
 */
@RequestMapping("/admin/file")
@RestController
public class UploadController {

    private static final Logger LOG = LoggerFactory.getLogger(UploadController.class);
    public  static final String BUSINESS_NAME ="文件上传";

    @Value("${file.path}")
    private String FILE_PATH;

    @Value("${file.domain}")
    private String FILE_DOMAIN;

    @Resource
    private FileService fileService;

    @RequestMapping("/upload")
    public ResponseDto upload(@RequestBody FileDto fileDto) throws Exception {
        try {
            ResponseDto<FileDto> responseDto = new ResponseDto<>();
            saveToTmpDir(fileDto);
            if (fileDto.getShardIndex().equals(fileDto.getShardTotal())) {
                merge(fileDto);
                fileService.save(fileDto);
                fileDto.setPath(FILE_DOMAIN + fileDto.getPath());
            }
            responseDto.setContent(fileDto);
            return responseDto;
        } catch (Exception e) {
            LOG.error(e.getMessage());
            throw new ValidatorException("上传文件出现错误！");
        }
    }

    /**
     * 保存到临时文件
     * @param fileDto FileDTO
     * @throws IOException  IOException
     */
    private void saveToTmpDir(FileDto fileDto) throws IOException {
        String tmpName = fileDto.getName().split("\\.")[0];
        String path = FILE_PATH + tmpName.substring(0, Math.min(tmpName.length(), 7));
        File dir = new File(path);
        // 创建临时目录
        if (!dir.exists()) {
            boolean ok = dir.mkdir();
            if (!ok) throw new IOException("创建目录失败！");
        }

        String filePath = path + "/" + fileDto.getShardIndex() + "_" + tmpName.substring(0, 5);
        File file = new File(filePath);
        // 创建临时文件
        if (!file.exists()) {
            boolean ok = file.createNewFile();
            if (!ok) throw new IOException("创建临时文件失败！");
        }
        String base64 = fileDto.getShard();
        // 解析base64编码文件
        MultipartFile data = base64ToMultipart(base64);
        // 转存到临时文件里面
        data.transferTo(file);
    }

    /**
     * base64转为multipart文件
     * @param base64 base64编码树
     * @return MultipartFile
     * @throws IOException  IOException
     */
    private MultipartFile base64ToMultipart(String base64) throws IOException {
        String[] strings = base64.split(",");
        String baseValue = base64.replaceAll(" ", "+");
        BASE64Decoder decoder = new BASE64Decoder();
        byte[] bytes;
        bytes = decoder.decodeBuffer(baseValue.replace(strings[0] + ",",""));
        for (int i = 0; i < bytes.length; ++i) {
            if (bytes[i] < 0) {
                bytes[i] += 256;
            }
        }
        return new Base64ToMultipartFile(bytes, strings[0]);
    }

    /**
     * 合并分片
     * @param fileDto FileDTO
     * @throws Exception Exception
     */
    public void merge(FileDto fileDto) throws Exception {
        LOG.info("合并分片开始");
        String tmpName = fileDto.getName().split("\\.")[0];
        // 设置限制不超过7和字符串长度的最小值
        int limit = Math.min(tmpName.length(), 7);
        String path = FILE_PATH + tmpName.substring(0, limit);
        String name = UuidUtil.getUuid();
        String targetName = path + "/" + name + "." + fileDto.getSuffix();
        File target = new File(targetName);
        if (!target.exists()){
            target.createNewFile();
        }
        BufferedOutputStream os = null;
        try {
            os = new BufferedOutputStream(new FileOutputStream(target));
            for (int index = 1; index <= fileDto.getShardTotal(); index ++ ) {
                // 设置限制不超过5和字符串长度的最小值
                int length = Math.min(5, fileDto.getName().length());
                String fileName = path + "/" + index + "_" + fileDto.getName().substring(0, length);
                File file = new File(fileName);
                while (!file.exists()) {
                    Thread.sleep(100);
                }
                BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
                // 读取文件并写到目标文件
                int reads = -1;
                byte[] bytes = new byte[1024];
                while ((reads = bis.read(bytes)) != -1) {
                    os.write(bytes);
                }
                os.flush();
            }
            // 设置文件
            String relativePath = tmpName.substring(0, limit) + "/" + name + "." + fileDto.getSuffix();
            fileDto.setPath(relativePath);
        } finally {
            try {
                if (os != null) {
                    os.close();
                }
            } catch (IOException e) {
                throw new IOException(e.getMessage());
            }
        }
    }

    @GetMapping("/check/{key}")
    public ResponseDto check(@PathVariable String key) throws Exception {
        LOG.info("检查上传分片开始：{}", key);
        try {
            ResponseDto<FileDto> responseDto = new ResponseDto<>();
            FileDto fileDto = fileService.findByKey(key);
            if (ObjectUtils.isEmpty(fileDto)) {
                return responseDto;
            }
            fileDto.setPath(FILE_DOMAIN + fileDto.getPath());
            responseDto.setContent(fileDto);
            return responseDto;
        } catch (Exception e) {
            LOG.error(e.getMessage());
            throw new ValidatorException("发生异常错误，");
        }
    }

}//end class
